#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
自定义matplotlib画布，支持Ctrl+滚轮缩放功能
"""

import logging
from PySide6.QtCore import Qt
from PySide6.QtGui import QWheelEvent

try:
    from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas
    HAS_MATPLOTLIB = True
except ImportError:
    HAS_MATPLOTLIB = False
    FigureCanvas = None

logger = logging.getLogger("chart_canvas")


class ZoomableFigureCanvas(FigureCanvas):
    """支持Ctrl+滚轮缩放的matplotlib画布"""
    
    def __init__(self, figure, parent=None):
        """初始化画布
        
        Args:
            figure: matplotlib图形对象
            parent: 父组件
        """
        if not HAS_MATPLOTLIB:
            raise ImportError("matplotlib is required for ZoomableFigureCanvas")
            
        super().__init__(figure)
        self.setParent(parent)
        
        # 缩放参数
        self.zoom_factor = 1.1  # 每次滚轮缩放的倍数
        self.min_zoom = 0.1     # 最小缩放倍数
        self.max_zoom = 10.0    # 最大缩放倍数
        
        logger.info("初始化支持Ctrl+滚轮缩放的matplotlib画布")
    
    def wheelEvent(self, event: QWheelEvent):
        """处理滚轮事件
        
        Args:
            event: 滚轮事件
        """
        # 检查是否按下了Ctrl键
        if event.modifiers() & Qt.ControlModifier:
            # 获取滚轮滚动方向
            delta = event.angleDelta().y()
            
            # 获取鼠标在图表中的位置
            pos = event.position()
            x_data, y_data = self._get_data_coordinates(pos.x(), pos.y())
            
            if x_data is not None and y_data is not None:
                # 执行缩放
                if delta > 0:
                    # 向上滚动，放大
                    self._zoom_at_point(x_data, y_data, self.zoom_factor)
                else:
                    # 向下滚动，缩小
                    self._zoom_at_point(x_data, y_data, 1.0 / self.zoom_factor)
                
                # 重绘图表
                self.draw()
                
                # 阻止事件继续传播
                event.accept()
                return
        
        # 如果不是Ctrl+滚轮，调用父类方法
        super().wheelEvent(event)
    
    def _get_data_coordinates(self, x_pixel, y_pixel):
        """将像素坐标转换为数据坐标
        
        Args:
            x_pixel: 像素x坐标
            y_pixel: 像素y坐标
            
        Returns:
            tuple: (x_data, y_data) 数据坐标，如果转换失败返回(None, None)
        """
        try:
            # 获取当前的坐标轴
            axes = self.figure.get_axes()
            if not axes:
                return None, None
            
            # 使用第一个坐标轴进行转换
            ax = axes[0]
            
            # 将像素坐标转换为数据坐标
            inv = ax.transData.inverted()
            x_data, y_data = inv.transform((x_pixel, y_pixel))
            
            return x_data, y_data
            
        except Exception as e:
            logger.warning(f"坐标转换失败: {e}")
            return None, None
    
    def _zoom_at_point(self, x_center, y_center, zoom_factor):
        """在指定点进行缩放
        
        Args:
            x_center: 缩放中心点x坐标（数据坐标）
            y_center: 缩放中心点y坐标（数据坐标）
            zoom_factor: 缩放倍数
        """
        try:
            # 获取当前的坐标轴
            axes = self.figure.get_axes()
            if not axes:
                return
            
            # 对所有坐标轴进行缩放
            for ax in axes:
                # 获取当前的坐标轴范围
                xlim = ax.get_xlim()
                ylim = ax.get_ylim()
                
                # 计算当前范围的宽度和高度
                x_width = xlim[1] - xlim[0]
                y_height = ylim[1] - ylim[0]
                
                # 计算新的范围宽度和高度
                new_x_width = x_width / zoom_factor
                new_y_height = y_height / zoom_factor
                
                # 检查缩放限制
                current_zoom_x = self._get_initial_x_range() / new_x_width if hasattr(self, '_initial_x_range') else 1.0
                current_zoom_y = self._get_initial_y_range() / new_y_height if hasattr(self, '_initial_y_range') else 1.0
                
                if (current_zoom_x > self.max_zoom or current_zoom_y > self.max_zoom or
                    current_zoom_x < self.min_zoom or current_zoom_y < self.min_zoom):
                    logger.debug("达到缩放限制，跳过此次缩放")
                    continue
                
                # 计算新的坐标轴范围，以鼠标位置为中心
                x_left = x_center - new_x_width * (x_center - xlim[0]) / x_width
                x_right = x_center + new_x_width * (xlim[1] - x_center) / x_width
                
                y_bottom = y_center - new_y_height * (y_center - ylim[0]) / y_height
                y_top = y_center + new_y_height * (ylim[1] - y_center) / y_height
                
                # 设置新的坐标轴范围
                ax.set_xlim(x_left, x_right)
                ax.set_ylim(y_bottom, y_top)
                
                logger.debug(f"缩放完成: x范围({x_left:.2f}, {x_right:.2f}), y范围({y_bottom:.2f}, {y_top:.2f})")
                
        except Exception as e:
            logger.error(f"缩放操作失败: {e}")
    
    def _get_initial_x_range(self):
        """获取初始x轴范围（用于计算缩放倍数）"""
        if not hasattr(self, '_initial_x_range'):
            axes = self.figure.get_axes()
            if axes:
                xlim = axes[0].get_xlim()
                self._initial_x_range = xlim[1] - xlim[0]
            else:
                self._initial_x_range = 1.0
        return self._initial_x_range
    
    def _get_initial_y_range(self):
        """获取初始y轴范围（用于计算缩放倍数）"""
        if not hasattr(self, '_initial_y_range'):
            axes = self.figure.get_axes()
            if axes:
                ylim = axes[0].get_ylim()
                self._initial_y_range = ylim[1] - ylim[0]
            else:
                self._initial_y_range = 1.0
        return self._initial_y_range
    
    def reset_zoom(self):
        """重置缩放到初始状态"""
        try:
            # 获取所有坐标轴
            axes = self.figure.get_axes()
            for ax in axes:
                ax.autoscale()
            
            # 重绘图表
            self.draw()
            
            # 重置初始范围缓存
            if hasattr(self, '_initial_x_range'):
                delattr(self, '_initial_x_range')
            if hasattr(self, '_initial_y_range'):
                delattr(self, '_initial_y_range')
                
            logger.info("缩放已重置")
            
        except Exception as e:
            logger.error(f"重置缩放失败: {e}") 